#include "widgetaudioin.h"
#include "ui_widgetaudioin.h"
#include <QDebug>       //调试输出
#include <QMessageBox>  //消息框
#include <QIntValidator>//限定端口号范围

WidgetAudioIn::WidgetAudioIn(QWidget *parent) :
    QWidget(parent),
    ui(new Ui::WidgetAudioIn)
{
    ui->setupUi(this);
    //初始化指针
    m_pInput = NULL;
    m_pSocket = NULL;
    m_pBuffDevice = NULL;
    //采样点类型列表，序号就是类型数值 0,1,2,3
    m_listSampleTypes<<"Unknown"<<"SignedInt"<<"UnSignedInt"<<"Float";
    //初始化，获取音频设备
    InitFindDevices();
}

WidgetAudioIn::~WidgetAudioIn()
{
    //这个缓冲设备对象归音频输入对象管理，不要手动删除它
    m_pBuffDevice = NULL;
    //删除音频输入设备和套接字
    if(NULL != m_pInput)
    {
        delete m_pInput;    m_pInput  = NULL;
    }
    if(NULL != m_pSocket)
    {
        delete m_pSocket;   m_pSocket = NULL;
    }

    //销毁窗口
    delete ui;
}

//初始化，获取音频设备
void WidgetAudioIn::InitFindDevices()
{
    //获取设备列表
    m_listDevices = QAudioDeviceInfo::availableDevices( QAudio::AudioInput );
    //添加到组合框
    for(int i=0; i<m_listDevices.count(); i++)
    {
        ui->comboBoxDeviceIn->addItem( m_listDevices[i].deviceName() );
    }
    //设置默认文件名，要用右斜杠
    ui->lineEditFile->setText( "E:/audio_8k1c8bit.pcm" );
    //设置默认IP和端口
    ui->lineEditIP->setText( "127.0.0.1" );
    ui->lineEditPort->setText( "12345" );
    //限定端口号范围
    QIntValidator *vali = new QIntValidator(1, 65535);
    ui->lineEditPort->setValidator( vali );
}

//设备名变化时，自动更新设备支持的采样参数
void WidgetAudioIn::on_comboBoxDeviceIn_currentIndexChanged(int index)
{
    if(index < 0)   return; //序号不合法，不处理
    //序号合法
    //根据设备添加支持的采样频率
    QList<int> listSampleRates = m_listDevices[index].supportedSampleRates();
    //清空旧的采样频率列表
    ui->comboBoxSampleRate->clear();
    //添加新的
    for(int i=0; i<listSampleRates.count(); i++)
    {
        ui->comboBoxSampleRate->addItem( tr("%1").arg(listSampleRates[i]) );
    }
    //通道数
    QList<int> listChannels = m_listDevices[index].supportedChannelCounts();
    //清空旧的通道数列表
    ui->comboBoxChannelCount->clear();
    //添加新的
    for(int i=0; i<listChannels.count(); i++)
    {
        ui->comboBoxChannelCount->addItem( tr("%1").arg(listChannels[i]) );
    }
    //采样位宽
    QList<int> listSizes = m_listDevices[index].supportedSampleSizes();
    //清空旧的位宽列表
    ui->comboBoxSampleSize->clear();
    //添加新的
    for(int i=0; i<listSizes.count(); i++)
    {
        ui->comboBoxSampleSize->addItem( tr("%1").arg(listSizes[i]) );
    }
    //采样点数据类型
    QList<QAudioFormat::SampleType> listTypes = m_listDevices[index].supportedSampleTypes();
    //清空旧的类型列表
    ui->comboBoxSampleType->clear();
    //添加新的
    for(int i=0; i<listTypes.count(); i++)
    {
        QString strType = m_listSampleTypes[ listTypes[i] ];
        //第一个参数是显示字符串，第二个参数是条目关联的用户数据，保存采样类型数值
        ui->comboBoxSampleType->addItem(strType, listTypes[i] );
    }
    //如果默认位宽是 8，设置默认用无符号整数
    if( 8 == listSizes[0] )
    {
        //无符号整数
        ui->comboBoxSampleType->setCurrentText( m_listSampleTypes[2] );
    }
    else
    {
        //其他情况默认用有符号整数
        ui->comboBoxSampleType->setCurrentText( m_listSampleTypes[1] );
    }
}

//开始录音和UDP发送
void WidgetAudioIn::on_pushButtonStart_clicked()
{
    //如果没有声卡不处理
    if( m_listDevices.count() < 1 )
    {
        return;
    }
    //先尝试打开文件
    m_fileSave.setFileName( ui->lineEditFile->text() );
    if( ! m_fileSave.open( QIODevice::WriteOnly ) )
    {
        //无法打开
        QMessageBox::warning(this, tr("打开文件"), tr("无法打开指定文件名，不能开始录音。"));
        return;
    }
    //获取IP和端口
    QString strIP = ui->lineEditIP->text();
    int nPort = ui->lineEditPort->text().toUShort();
    QHostAddress dstHost;//网络地址对象
    bool bRet = dstHost.setAddress(strIP); //尝试把字符串转为IP地址格式
    if( !bRet )
    {
        //转换失败
        QMessageBox::warning(this, tr("IP设置"), tr("IP地址不合法，请重新设置IP。"));
        m_fileSave.close(); //关闭之前打开的文件
        return;
    }
    //新建网络套接字
    m_pSocket = new QUdpSocket();
    //建立连接，UDP的连接函数仅用于设置目标IP和端口
    m_pSocket->connectToHost(dstHost, nPort);

    //文件和网络的准备工作就绪
    //选择的声卡
    QAudioDeviceInfo dev = m_listDevices[ ui->comboBoxDeviceIn->currentIndex() ];
    //设置音频采样格式
    //采样频率
    m_aFormat.setSampleRate( ui->comboBoxSampleRate->currentText().toInt() );
    //通道数
    m_aFormat.setChannelCount( ui->comboBoxChannelCount->currentText().toInt() );
    //采样位宽
    m_aFormat.setSampleSize( ui->comboBoxSampleSize->currentText().toInt() );
    //采样点数据类型
    QAudioFormat::SampleType curType =
            (QAudioFormat::SampleType)( ui->comboBoxSampleType->currentData().toInt() );
    m_aFormat.setSampleType( curType );
    //字节序不用设置，用系统默认值
    //编码格式固定用PCM
    m_aFormat.setCodec( "audio/pcm" );
    //判断格式是否支持
    if( ! dev.isFormatSupported( m_aFormat ) )
    {
        qDebug()<<"old audioFomat is not supported: "<<endl<<m_aFormat;
        //获取近似支持的格式
        m_aFormat = dev.nearestFormat( m_aFormat );
    }
    qDebug()<<"used Format: "<<endl<<m_aFormat<<endl;
    //新建
    m_pInput = new QAudioInput(dev, m_aFormat);
    //关联状态变化的信号到槽函数
    connect(m_pInput, SIGNAL(stateChanged(QAudio::State)),
            this, SLOT(onDeviceStateChanged(QAudio::State)) );
    //开始采集
    m_pBuffDevice = m_pInput->start();
    //关联缓冲区数据可读信号到槽函数
    connect(m_pBuffDevice, SIGNAL(readyRead()),
            this, SLOT(onBuffReadyRead()) );
    //开始采样后，禁止修改采样参数
    holdParas(true);
}

//开始采样时，禁止修改采样参数，停止后恢复
void WidgetAudioIn::holdParas(bool bHold)
{
    if( bHold )
    {
        //组合框和编辑框全部禁用
        ui->comboBoxDeviceIn->setEnabled( false );
        ui->comboBoxSampleRate->setEnabled( false );
        ui->comboBoxChannelCount->setEnabled( false );
        ui->comboBoxSampleSize->setEnabled( false );
        ui->comboBoxSampleType->setEnabled( false );
        ui->lineEditFile->setEnabled( false );
        ui->lineEditIP->setEnabled( false );
        ui->lineEditPort->setEnabled( false );
        //开始按钮也禁用
        ui->pushButtonStart->setEnabled( false );
    }
    else
    {
        //组合框和编辑框全部启用
        ui->comboBoxDeviceIn->setEnabled( true );
        ui->comboBoxSampleRate->setEnabled( true );
        ui->comboBoxChannelCount->setEnabled( true );
        ui->comboBoxSampleSize->setEnabled( true );
        ui->comboBoxSampleType->setEnabled( true );
        ui->lineEditFile->setEnabled( true );
        ui->lineEditIP->setEnabled( true );
        ui->lineEditPort->setEnabled( true );
        //开始按钮也启用
        ui->pushButtonStart->setEnabled( true );
    }
}

void WidgetAudioIn::onDeviceStateChanged(QAudio::State state)
{
    //打印状态变化
    qDebug()<<state<<endl;
    if( QAudio::NoError != m_pInput->error() )
    {
        //打印错误编号
        qDebug()<<"Error: "<<m_pInput->error()<<endl;
    }
}

void WidgetAudioIn::onBuffReadyRead()
{
    qDebug()<<"Bytes ready: "<<m_pInput->bytesReady();
    //读取缓冲区
    QByteArray baData = m_pBuffDevice->readAll();
    //写入文件
    m_fileSave.write( baData );
    //发送网络
    m_pSocket->write( baData );
}

//停止
void WidgetAudioIn::on_pushButtonStop_clicked()
{
    if( NULL == m_pInput )
    {
        //没有开始，直接返回
        return;
    }
    //停止录音
    m_pInput->stop();
    //删除录音对象
    delete m_pInput;    m_pInput = NULL;
    //停止网络
    m_pSocket->disconnectFromHost();
    //删除套接字
    delete m_pSocket;   m_pSocket = NULL;
    //关闭文件
    m_fileSave.close();
    //缓冲区设备对象归录音对象管理，不要手动删除，把指针设置为 NULL 即可
    m_pBuffDevice = NULL;

    //恢复组合框、编辑框、开始按钮
    holdParas(false);
}
